package com.zzg.mybatis.generator.controller;

import com.zzg.mybatis.generator.bridge.MybatisGeneratorBridge;
import com.zzg.mybatis.generator.model.DatabaseConfig;
import com.zzg.mybatis.generator.model.GeneratorConfig;
import com.zzg.mybatis.generator.model.TableFxModel;
import com.zzg.mybatis.generator.model.UITableColumnVO;
import com.zzg.mybatis.generator.util.ConfigHelper;
import com.zzg.mybatis.generator.util.DbUtil;
import com.zzg.mybatis.generator.view.AlertUtil;
import com.zzg.mybatis.generator.view.UIProgressCallback;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
import javafx.fxml.FXML;
import javafx.geometry.Pos;
import javafx.scene.Cursor;
import javafx.scene.control.*;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.control.cell.TextFieldTreeCell;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox;
import javafx.util.Callback;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.mybatis.generator.config.ColumnOverride;
import org.mybatis.generator.config.IgnoredColumn;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.awt.*;
import java.io.File;
import java.net.URL;
import java.sql.SQLRecoverableException;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;

public class MainUIController extends BaseFXController {
    private static final String VO_GENERATE = "_vo_generate_";
    private static final String OTHER_GENERATE = "_other_generate_";
    private static final Logger _LOG = LoggerFactory.getLogger(MainUIController.class);
    private static final String FOLDER_NO_EXIST = "部分目录不存在，是否创建";
    private GeneratorConfig generatorConfig = null;

    @FXML
    private Label connectionLabel;
    @FXML
    private Label configsLabel;
    @FXML
    private Checkbox checkBoxSetting;
    @FXML
    private Label voConvertLabel;
    @FXML
    private Label domainConvertLabel;
    @FXML
    private Label searchBoxLabel;
    @FXML
    private TextField searchBox;
    @FXML
    private TreeView<String> leftDBTree;
    @FXML
    private TableView<TableFxModel> tableView;
    @FXML
    private Tab tabForDataBase;

    /**
     * 支持多表操作，Key为表名，必须唯一
     */
    private Map<String, List<IgnoredColumn>> ignoredColumns = new HashMap<>();

    private List<ColumnOverride> columnOverrides;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        ImageView dbImage = new ImageView("icons/computer.png");
        dbImage.setFitHeight(25);
        dbImage.setFitWidth(25);
        connectionLabel.setGraphic(dbImage);
        connectionLabel.setOnMouseClicked(event -> {
            DbConnectionController controller = (DbConnectionController) loadFXMLPage("新建数据库连接", FXMLPage.NEW_CONNECTION, false);
            controller.setMainUIController(this);
            controller.showDialogStage();
        });
        ImageView configImage = new ImageView("icons/config-list.png");
        configImage.setFitHeight(25);
        configImage.setFitWidth(25);
        configsLabel.setGraphic(configImage);
        configsLabel.setOnMouseClicked(event -> {
            GeneratorConfigController controller = (GeneratorConfigController) loadFXMLPage("配置", FXMLPage.GENERATOR_CONFIG, false);
            controller.mainController = this;
            controller.showDialogStage();
        });

        ImageView reverseImage = new ImageView("icons/reverse.png");
        reverseImage.setFitHeight(25);
        reverseImage.setFitWidth(25);

        ImageView voImage = new ImageView("icons/vo.png");
        voImage.setFitHeight(25);
        voImage.setFitWidth(25);
        voConvertLabel.setGraphic(voImage);
        voConvertLabel.setOnMouseClicked(event -> {
            try {
                this.generateCodeCommon(VO_GENERATE);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        searchBox.setMinHeight(27);
        searchBox.setStyle("-fx-background-radius: 0;-fx-border-radius: 0px;");
        searchBox.setOnKeyPressed(event -> {
            if (event.getCode() == KeyCode.ENTER) {
                if (StringUtils.isNotBlank(this.searchBox.getText())) {
                    //TODO:
                    this.tableView.scrollTo(10);
                    this.tableView.getSelectionModel().select(10);
                    this.tableView.getColumns().size();

                }
            }
        });
        ImageView searchImage = new ImageView("icons/search.png");
        searchImage.setFitHeight(25);
        searchImage.setFitWidth(25);
        searchBoxLabel.setGraphic(searchImage);
        searchBoxLabel.setOnMouseClicked(event -> {
            this.searchWithLike(this.searchBox.getText());
        });
        ImageView domainImage = new ImageView("icons/domain.png");
        domainImage.setFitHeight(25);
        domainImage.setFitWidth(25);
        domainConvertLabel.setGraphic(domainImage);
        domainConvertLabel.setOnMouseClicked(event -> {
            try {
                this.generateCodeCommon(OTHER_GENERATE);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });

        ImageView connectionImage = new ImageView("icons/database.png");
        connectionImage.setFitHeight(15);
        connectionImage.setFitWidth(15);
        tabForDataBase.setGraphic(connectionImage);

        //treeView
        leftDBTree.setShowRoot(false);
        leftDBTree.setRoot(new TreeItem<>());
        this.tableView.setEditable(true);
        this.tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
        //初始化表格对应的列
        TableColumn<TableFxModel, String> serialColumn = new TableColumn<>("序号");
        TableColumn<TableFxModel, String> codeColumn = new TableColumn<>("编号");
        TableColumn<TableFxModel, String> nameColumn = new TableColumn<>("名称");
        TableColumn<TableFxModel, String> interfaceColumn = new TableColumn<>("接口名称");
        TableColumn<TableFxModel, String> domainColumn = new TableColumn<>("实体");
        TableColumn<TableFxModel, Boolean> checkBoxColumn = new TableColumn<>("选择");
        TableColumn<TableFxModel, TableFxModel> operationColumn = new TableColumn<>("操作");
        //初始化列宽
        serialColumn.setPrefWidth(40);
        serialColumn.setMinWidth(40);
        serialColumn.setMaxWidth(40);
        codeColumn.setPrefWidth(200);
        nameColumn.setPrefWidth(150);
        interfaceColumn.setPrefWidth(150);
        domainColumn.setPrefWidth(150);
        operationColumn.setPrefWidth(150);
        checkBoxColumn.setPrefWidth(30);
        //设置列的特性，如只读、编辑等
        domainColumn.setEditable(true);
        interfaceColumn.setEditable(true);
        checkBoxColumn.setEditable(true);

        serialColumn.setCellValueFactory(new PropertyValueFactory<>("serialNumber"));
        codeColumn.setCellValueFactory(new PropertyValueFactory<>("tableName"));
        nameColumn.setCellValueFactory(new PropertyValueFactory<>("remark"));
        interfaceColumn.setCellValueFactory(new PropertyValueFactory<>("interfaceName"));
        domainColumn.setCellValueFactory(new PropertyValueFactory<>("domainName"));

        domainColumn.setCellFactory(TextFieldTableCell.forTableColumn());
        domainColumn.setOnEditCommit((TableColumn.CellEditEvent<TableFxModel, String> event) -> {
            TablePosition<TableFxModel, String> pos = event.getTablePosition();
            String newDomainName = event.getNewValue();
            int row = pos.getRow();
            TableFxModel person = event.getTableView().getItems().get(row);
            person.setDomainName(newDomainName);
        });
        //排序类型设置
        codeColumn.setSortType(TableColumn.SortType.DESCENDING);
        nameColumn.setSortType(TableColumn.SortType.DESCENDING);
        domainColumn.setSortType(TableColumn.SortType.DESCENDING);

        checkBoxColumn.setCellValueFactory(param -> {
            TableFxModel tableFxModel = param.getValue();

            SimpleBooleanProperty booleanProp = new SimpleBooleanProperty(tableFxModel.getChecked());
            booleanProp.addListener((observable, oldValue, newValue) -> tableFxModel.setChecked(newValue));
            return booleanProp;
        });

        checkBoxColumn.setCellFactory(p -> {
            CheckBoxTableCell<TableFxModel, Boolean> cell = new CheckBoxTableCell<>();
            cell.setAlignment(Pos.CENTER);
            return cell;
        });
        operationColumn.setCellValueFactory(new PropertyValueFactory<>("tableName"));

        operationColumn.setCellFactory(cell -> new TableCell() {
            @Override
            protected void updateItem(Object item, boolean empty) {
                editableProperty().set(true);
                super.updateItem(item, empty);
                if (item == null || empty) {
                    setText(null);
                    setGraphic(null);
                } else {
                    Label label0 = new Label();
                    ImageView imageView = new ImageView("icons/use.png");
                    imageView.setFitHeight(15);
                    imageView.setFitWidth(15);
                    label0.setText("定制列");
                    label0.setGraphic(imageView);

                    HBox hBox = new HBox();
                    hBox.setSpacing(10);
                    hBox.getChildren().add(label0);
                    label0.setCursor(Cursor.HAND);
                    label0.setMinWidth(150);
                    label0.setOnMouseClicked(event -> {
                        try {
                            openTableColumnCustomizationPage(item.toString());
                        } catch (Exception e) {
                            AlertUtil.showErrorAlert(e.getMessage());
                        }
                    });
                    setGraphic(hBox);
                }
            }
        });

        //添加列
        this.tableView.getColumns().setAll(serialColumn, codeColumn, nameColumn, domainColumn, operationColumn);
        //显示动态列控制
        this.tableView.setTableMenuButtonVisible(true);
        //TableView的一些设置项
        this.tableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
        this.tableView.getSelectionModel().setCellSelectionEnabled(false);
        //CheckBox居中显示
        checkBoxColumn.setStyle("-fx-alignment:CENTER");
        this.tableView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
            //TODO:待优化
        });
        //双击事件
        this.tableView.setRowFactory(param -> {
            TableRow<TableFxModel> row = new TableRow<>();
            row.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> {
                if (event.getClickCount() == 2) {
                    //TODO:双击执行内容
                }
            });
            return row;
        });
        Callback<TreeView<String>, TreeCell<String>> defaultCellFactory = TextFieldTreeCell.forTreeView();
        leftDBTree.setCellFactory((TreeView<String> tv) -> {
            TreeCell<String> cell = defaultCellFactory.call(tv);
            cell.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> {
                int level = leftDBTree.getTreeItemLevel(cell.getTreeItem());
                TreeCell<String> treeCell = (TreeCell<String>) event.getSource();
                TreeItem<String> treeItem = treeCell.getTreeItem();
                if (treeItem == null)
                    return;
                if (level == 1) {
                    final ContextMenu contextMenu = new ContextMenu();
                    MenuItem item0 = new MenuItem("重新连接");
                    item0.setOnAction(event1 -> {
                        //TODO:
                    });
                    MenuItem item1 = new MenuItem("关闭连接");
                    item1.setOnAction(event1 -> treeItem.getChildren().clear());
                    MenuItem item2 = new MenuItem("编辑连接");
                    item2.setOnAction(event1 -> {
                        DatabaseConfig selectedConfig = (DatabaseConfig) treeItem.getGraphic().getUserData();
                        DbConnectionController controller = (DbConnectionController) loadFXMLPage("编辑数据库连接", FXMLPage.NEW_CONNECTION, false);
                        controller.setMainUIController(this);
                        controller.setConfig(selectedConfig);

                        controller.showDialogStage();
                    });
                    MenuItem item3 = new MenuItem("删除连接");
                    item3.setOnAction(event1 -> {
                        DatabaseConfig selectedConfig = (DatabaseConfig) treeItem.getGraphic().getUserData();
                        try {
                            ConfigHelper.deleteDatabaseConfig(selectedConfig);
                            this.loadLeftDBTree();
                            //TODO:清楚数据源
                        } catch (Exception e) {
                            AlertUtil.showErrorAlert("Delete connection failed! Reason: " + e.getMessage());
                        }
                    });
                    //图标的封装
                    ImageView imageView = new ImageView("icons/reload.png");
                    imageView.setFitWidth(16);
                    imageView.setFitHeight(16);
                    item0.setGraphic(imageView);

                    imageView = new ImageView("icons/close.png");
                    imageView.setFitWidth(16);
                    imageView.setFitHeight(16);
                    item1.setGraphic(imageView);

                    imageView = new ImageView("icons/pencil.png");
                    imageView.setFitWidth(16);
                    imageView.setFitHeight(16);
                    item2.setGraphic(imageView);

                    imageView = new ImageView("icons/delete.png");
                    imageView.setFitWidth(16);
                    imageView.setFitHeight(16);
                    item3.setGraphic(imageView);
                    contextMenu.getItems().addAll(item0, item1, item2, item3);
                    cell.setContextMenu(contextMenu);
                }
                if (event.getClickCount() == 2) {
                    treeItem.setExpanded(true);
                    if (level == 1) {
                        System.out.println("index: " + leftDBTree.getSelectionModel().getSelectedIndex());
                        DatabaseConfig selectedConfig = (DatabaseConfig) treeItem.getGraphic().getUserData();
                        //进行右侧的表数据加载
                        this.loadTreeFromDb(selectedConfig);
                    }
                }
            });
            return cell;
        });
        loadLeftDBTree();
    }

    /**
     * @param generatorConfig
     */
    public void setGeneratorConfig(GeneratorConfig generatorConfig) {
        this.generatorConfig = generatorConfig;
    }

    /**
     * 重新根据数据库连接加载数据表，相当于刷新功能
     *
     * @param databaseConfig
     */
    private void loadTreeFromDb(DatabaseConfig databaseConfig) {
        try {
            List<TableFxModel> tables = DbUtil.getTableNames(databaseConfig);
            this.tableView.setItems(null);
            if (tables != null && tables.size() > 0) {
                List<TableFxModel> tableFxModels = new ArrayList<>();

                for (TableFxModel tableFxModel : tables) {
                    tableFxModel.setDomainName(tableFxModel.getTableName());
                    tableFxModel.setInterfaceName("I" + tableFxModel.getTableName());

                    tableFxModels.add(tableFxModel);
                }

                ObservableList<TableFxModel> list = FXCollections.observableArrayList(tableFxModels);


                FilteredList<TableFxModel> filteredData = new FilteredList<>(list, b -> true);
                this.searchBox.textProperty().addListener((observable, oldValue, newValue) -> {
                    filteredData.setPredicate(tableFxModel -> {
                        if (newValue == null || newValue.isEmpty()) return true;
                        String lowerCaseFilter = newValue.toLowerCase();
                        if (tableFxModel.getTableName().toLowerCase().indexOf(lowerCaseFilter) != -1)
                            return true;
                        else if (tableFxModel.getRemark().toLowerCase().indexOf(lowerCaseFilter) != -1)
                            return true;
                        else return false;
                    });
                });
                SortedList<TableFxModel> sortedData = new SortedList<>(filteredData);
                sortedData.comparatorProperty().bind(this.tableView.comparatorProperty());
                this.tableView.setItems(sortedData);
            }
        } catch (SQLRecoverableException e) {
            _LOG.error(e.getMessage(), e);
            AlertUtil.showErrorAlert("连接超时");
        } catch (Exception e) {
            _LOG.error(e.getMessage(), e);
            AlertUtil.showErrorAlert(e.getMessage());
        }
    }

    protected void loadLeftDBTree() {
        TreeItem rootTreeItem = leftDBTree.getRoot();
        rootTreeItem.getChildren().clear();
        try {
            List<DatabaseConfig> dbConfigs = ConfigHelper.loadDatabaseConfig();
            for (DatabaseConfig dbConfig : dbConfigs) {
                TreeItem<String> treeItem = new TreeItem<>();
                treeItem.setValue(dbConfig.getName());
                ImageView dbImage = new ImageView("icons/computer.png");
                dbImage.setFitHeight(16);
                dbImage.setFitWidth(16);
                dbImage.setUserData(dbConfig);
                treeItem.setGraphic(dbImage);
                rootTreeItem.getChildren().add(treeItem);
            }
        } catch (Exception e) {
            _LOG.error("connect db failed, reason: {}", e);
            AlertUtil.showErrorAlert(e.getMessage() + "\n" + ExceptionUtils.getStackTrace(e));
        }
    }

    private void searchWithLike(String keywords) {
        Object s = leftDBTree;
    }

    private void generateCodeCommon(String currentOperation) throws Exception {
        if (this.tableView.getSelectionModel() == null || this.tableView.getSelectionModel().getSelectedItems() == null || this.tableView.getSelectionModel().getSelectedItems().stream().count() <= 0) {
            AlertUtil.showWarnAlert("请选择索要操作的数据库表");
            return;
        }
        List<TableFxModel> tableFxModels = this.tableView.getSelectionModel().getSelectedItems().stream().collect(Collectors.toList());

        String result = validateConfig();
        if (result != null) {
            AlertUtil.showErrorAlert(result);
            return;
        }
        if (!checkDirs(generatorConfig)) {
            return;
        }

        MybatisGeneratorBridge bridge = new MybatisGeneratorBridge();
        bridge.setColumnOverrides(columnOverrides);
        UIProgressCallback alert = new UIProgressCallback(Alert.AlertType.INFORMATION);
        bridge.setProgressCallback(alert);
        alert.show();

        bridge.generateVo(generatorConfig, tableFxModels, ignoredColumns, currentOperation);
    }

    private String validateConfig() {
        if (this.generatorConfig == null)
            return "请选择配置";
        String projectFolder = this.generatorConfig.getProjectFolder();
        String modelPackage = this.generatorConfig.getModelPackage();
        String mappingXMLPackage = this.generatorConfig.getMappingXMLPackage();
        String daoPackage = this.generatorConfig.getDaoPackage();

        if (StringUtils.isEmpty(projectFolder)) {
            return "项目目录不能为空";
        }
        if (StringUtils.isAnyEmpty(modelPackage, mappingXMLPackage, daoPackage)) {
            return "包名不能为空";
        }

        return null;
    }

    /**
     * 定制实体或者Vo对象生成的字段定制 TODO：需要增加entity的控制，entity禁止定制列，防止漏字段
     *
     * @param tableName 表名称
     */
    public void openTableColumnCustomizationPage(String tableName) {
        if (this.leftDBTree.getSelectionModel() == null || this.leftDBTree.getSelectionModel().getSelectedItem() == null || this.leftDBTree.getSelectionModel().getSelectedItem().getGraphic() == null)
            return;
        DatabaseConfig databaseConfig = (DatabaseConfig) this.leftDBTree.getSelectionModel().getSelectedItem().getGraphic().getUserData();
        SelectTableColumnController controller = (SelectTableColumnController) loadFXMLPage("定制列", FXMLPage.SELECT_TABLE_COLUMN, true);
        controller.setMainUIController(this);
        try {
            List<UITableColumnVO> tableColumns = DbUtil.getTableColumns(databaseConfig, tableName);
            controller.setColumnList(FXCollections.observableList(tableColumns));
            controller.setTableName(tableName);
            controller.showDialogStage();
        } catch (Exception e) {
            _LOG.error(e.getMessage(), e);
            AlertUtil.showErrorAlert(e.getMessage());
        }
    }

    /**
     * 定制列的控制
     *
     * @param ignoredColumns 忽略列的集合
     * @param tableName      表名
     */
    public void setIgnoredColumns(List<IgnoredColumn> ignoredColumns, String tableName) {
        if (this.ignoredColumns.containsKey(tableName))
            this.ignoredColumns.remove(tableName);
        this.ignoredColumns.put(tableName, ignoredColumns);
    }

    public void setColumnOverrides(List<ColumnOverride> columnOverrides) {
        this.columnOverrides = columnOverrides;
    }

    /**
     * 检查并创建不存在的文件夹
     *
     * @return
     */
    private boolean checkDirs(GeneratorConfig config) {
        List<String> dirs = new ArrayList<>();
        dirs.add(config.getProjectFolder());
        dirs.add(FilenameUtils.normalize(config.getProjectFolder().concat("/").concat(config.getModelPackageTargetFolder())));
        dirs.add(FilenameUtils.normalize(config.getProjectFolder().concat("/").concat(config.getDaoTargetFolder())));
        dirs.add(FilenameUtils.normalize(config.getProjectFolder().concat("/").concat(config.getMappingXMLTargetFolder())));
        boolean haveNotExistFolder = false;
        for (String dir : dirs) {
            File file = new File(dir);
            if (!file.exists()) {
                haveNotExistFolder = true;
            }
        }
        if (haveNotExistFolder) {
            Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
            alert.setContentText(FOLDER_NO_EXIST);
            Optional<ButtonType> optional = alert.showAndWait();
            if (optional.isPresent()) {
                if (ButtonType.OK == optional.get()) {
                    try {
                        for (String dir : dirs) {
                            FileUtils.forceMkdir(new File(dir));
                        }
                        return true;
                    } catch (Exception e) {
                        AlertUtil.showErrorAlert("创建目录失败，请检查目录是否是文件而非目录");
                    }
                } else {
                    return false;
                }
            }
        }
        return true;
    }
}
